/**
 * KopsoxFramework
 * Copyright (C) 2010 Free Software Foundation, Inc. <http://fsf.org/>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kopsox.util.validation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.kopsox.comm.message.ErrorMessage;
import org.kopsox.comm.message.ValueMessage;
import org.kopsox.general.priority.Priority;
import org.kopsox.general.priority.PriorityEnum;
import org.kopsox.validation.Validator;
import org.kopsox.validation.handler.ValidationErrorHandler;

/**
 * This is a utility class that can be used to delegate validation-work to a set of Validators.<br>
 * The Validators are always sorted in this Set
 * 
 * @author Konrad Renner - 08.11.2010
 * @see TreeSet
 */
public class ValidatorSet extends TreeSet<Validator> implements Validator {
	
	private static final long serialVersionUID = -6470531373695696263L;
	
	private Priority priority;
	private Set<ValidationErrorHandler> handlers;
	protected List<ErrorMessage> errorMessages;
	private boolean autoDelegation;
	
	public ValidatorSet() {
		super();
		init();
	}
	
	public ValidatorSet(Collection<Validator> validators) {
		super(validators);
		init();
	}
	
	private final void init() {
		this.priority = PriorityEnum.MEDIUM.getPriority();
		this.handlers = new HashSet<ValidationErrorHandler>();
		this.errorMessages = new ArrayList<ErrorMessage>();
		this.autoDelegation = false;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.validation.Validator#addHandler(org.kopsox.validation.handler.ValidationErrorHandler)
	 */
	@Override
	public void addHandler(ValidationErrorHandler handler) {
		handlers.add(handler);
	}

	/* (non-Javadoc)
	 * @see org.kopsox.validation.Validator#delegateErrorsToHandlers()
	 */
	@Override
	public void delegateErrorsToHandlers() {
		for(ValidationErrorHandler handler:this.handlers){
			handler.handleValidationError(this.errorMessages);
		}
	}

	/* (non-Javadoc)
	 * @see org.kopsox.validation.Validator#getValidationErrors()
	 */
	@Override
	public List<ErrorMessage> getValidationErrors() {
		return this.errorMessages;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.validation.Validator#removeHandler(org.kopsox.validation.handler.ValidationErrorHandler)
	 */
	@Override
	public void removeHandler(ValidationErrorHandler handler) {
		this.handlers.remove(handler);
	}

	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(Validator o) {
		if(this.priority.compareTo(o.getPriority()) == 0) {
			return getClass().getName().compareTo(o.getClass().getName());
		}
		return this.priority.compareTo(o.getPriority());
	}

	/* (non-Javadoc)
	 * @see org.kopsox.validation.Validator#validateValueMessage(org.kopsox.comm.message.ValueMessage)
	 */
	@Override
	public boolean validateValueMessage(ValueMessage value) {
		boolean validationOk = true;
		
		//Clear the messages
		this.errorMessages.clear();
		
		Iterator<Validator> iter = super.iterator();
		
		while(iter.hasNext()) {
			Validator val = iter.next();
			
			boolean ok = val.validateValueMessage(value);
			
			if(!ok) {
				this.errorMessages.addAll(val.getValidationErrors());
				
				validationOk = false;
				
				if(this.autoDelegation) {
					val.delegateErrorsToHandlers();
				}
				
				//if the Validator is an 'Show-Stopping'-Validtor, stop the process
				if(val.isShowStoppingValidator()) {
					break;
				}
					
			}
		}
		
		return validationOk;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.general.priority.Prioritized#getPriority()
	 */
	@Override
	public Priority getPriority() {
		return this.priority;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.general.priority.Prioritized#setPriority(org.kopsox.general.priority.Priority)
	 */
	@Override
	public void setPriority(final Priority prio) {
		this.priority = prio;
	}

	/**
	 * Sets a flag if the Handlers of the Validators should be called automatically<br>
	 * <b>NOTE:</b> This flag is used for the Validators of this Set, not for the Set itself
	 * 
	 * @param auto
	 */
	public void setAutomaticDelegationToHandlers(final boolean auto) {
		this.autoDelegation = auto;
	}
	
	/**
	 * Tells if the automatich Delegation to the Handlers of the Validators is enabled or not<br>
	 * <b>NOTE:</b> This flag is used for the Validators of this Set, not for the Set itself
	 * 
	 * @return boolean
	 */
	public boolean isAutomaticDelegationToHandlersEnabled() {
		return this.autoDelegation;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.validation.Validator#isShowStoppingValidator()
	 */
	@Override
	public boolean isShowStoppingValidator() {
		return false;
	}
	
	@Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + (autoDelegation ? 1231 : 1237);
        result = prime * result
                        + ((errorMessages == null) ? 0 : errorMessages.hashCode());
        result = prime * result
                        + ((handlers == null) ? 0 : handlers.hashCode());
        result = prime * result
                        + ((priority == null) ? 0 : priority.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
                return true;
        if (!super.equals(obj))
                return false;
        if (getClass() != obj.getClass())
                return false;
        ValidatorSet other = (ValidatorSet) obj;
        if (autoDelegation != other.autoDelegation)
                return false;
        if (errorMessages == null) {
                if (other.errorMessages != null)
                        return false;
        } else if (!errorMessages.equals(other.errorMessages))
                return false;
        if (handlers == null) {
                if (other.handlers != null)
                        return false;
        } else if (!handlers.equals(other.handlers))
                return false;
        if (priority == null) {
                if (other.priority != null)
                        return false;
        } else if (!priority.equals(other.priority))
                return false;
        return true;
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ValidatorSet[");
        sb.append("priority=");
        sb.append(priority);
        sb.append(",autoDelegation=");
        sb.append(autoDelegation);
        sb.append(",handlers");
        sb.append(handlers);
        sb.append(",errorMessages");
        sb.append(errorMessages);
        sb.append(',');
        sb.append(super.toString());
        sb.append(']');
        
        return sb.toString();
    }
}
